﻿using System;
using System.Collections.Generic;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Carbon.Web.Forms.Controls
{
    [ParseChildren(false)]
    [PersistChildren(true)]
    abstract public partial class PopupDataContextControl<TViewModel> : DataContextControl<TViewModel>
    {
		#region Fields (12)
		private TransferDataStyle m_TransferStyle = default(TransferDataStyle);

		private bool m_ControlsAdded = false;
		private bool m_FetchFromParent = false;
		private bool m_FetchFromParent_calculated = false;
		private bool m_IsClosing = default(bool);
		private bool m_PersistanceIdentifierKey_calculated = false;
		private bool m_PersistanceIdentifier_calculated = false;
		private bool m_TransferStyle_calculated = false;

		private List<Control> m_ChildControls = new List<Control>();

		private Guid m_PersistanceIdentifierKey = default(Guid);

        private String m_PersistanceIdentifier = string.Empty;

		private HiddenField ctrl_SerializedObject;
		#endregion

		#region Properties (8)
		protected HiddenField Control_SerializedObject
        {
            get
            {
                if (ctrl_SerializedObject == null) throw new NullReferenceException("ctrl_SerializedObject");
                return ctrl_SerializedObject;
            }
        }
		public virtual bool FetchFromParent
        {
            get
            {
                if (!m_FetchFromParent_calculated)
                {
                    String sValue = this.Page.Request.QueryString["FetchFromParent"];

                    if (string.IsNullOrEmpty(sValue))
                    {
                        m_FetchFromParent = true;
                    }
                    else
                    {
                        m_FetchFromParent = ConvertInvariant.ToBoolean(sValue);
                    }
                    m_FetchFromParent_calculated = true;
                }

                return m_FetchFromParent;
            }
            set
            {
                m_FetchFromParent = value;
                m_FetchFromParent_calculated = true;
            }
        }
		public bool IsClosing
        {
            get
            {
                return m_IsClosing;
            }
            set
            {
                m_IsClosing = value;
            }
        }
		public bool IsViewModelFetched
        {
            get
            {
                if (FetchFromParent)
                {
                    Object cachedIsViewModelFetched = ViewState["IsViewModelFetched"];

                    if (cachedIsViewModelFetched == null)
                    {
                        return false;
                    }

                    return (bool)cachedIsViewModelFetched;
                }
                else
                {
                    return true;
                }
            }
            set
            {
                this.TrackViewState();
                ViewState["IsViewModelFetched"] = value;
            }
        }
		public String PersistanceIdentifier
        {
            get
            {
                if (!m_PersistanceIdentifier_calculated)
                {
                    String persistanceIdentitifer = this.Page.Request.QueryString["PersistanceIdentifier"];

                    if (string.IsNullOrEmpty(persistanceIdentitifer)) throw new ArgumentOutOfRangeException("persistanceIdentitifer");

                    String[] values = persistanceIdentitifer.Split(',');
                    m_PersistanceIdentifier = values[0].Trim();
                    m_PersistanceIdentifier_calculated = true;
                }

                return m_PersistanceIdentifier;
            }
        }
		public Guid PersistanceIdentifierKey
        {
            get
            {
                if (!m_PersistanceIdentifierKey_calculated)
                {
                    String persistanceIdentitifer = this.Page.Request.QueryString["PersistanceIdentifier"];

                    if (string.IsNullOrEmpty(persistanceIdentitifer)) throw new ArgumentOutOfRangeException("persistanceIdentitifer");
                    String[] values = persistanceIdentitifer.Split(',');

                    m_PersistanceIdentifierKey = new Guid(values[1].Trim());
                    m_PersistanceIdentifierKey_calculated = true;
                }

                return m_PersistanceIdentifierKey;
            }
        }
		private String SerializedObjectFieldId
        {
            get
            {
                String serializedObjectFieldId = this.Page.Request.Params["soid"];
                if (string.IsNullOrEmpty(serializedObjectFieldId)) throw new ArgumentOutOfRangeException("serializedObjectFieldId");
                return serializedObjectFieldId;
            }
        }
		public TransferDataStyle TransferStyle
        {
            get
            {
                if (!m_TransferStyle_calculated)
                {
                    m_TransferStyle = (TransferDataStyle)ConvertInvariant.ToInt32(this.Page.Request.QueryString["TransferStyle"]);
                    m_TransferStyle_calculated = true;
                }

                return m_TransferStyle;
            }
        }
		#endregion

		#region Constructors (1)
		public PopupDataContextControl()
        {
        }
		#endregion

		#region ASP Processing

		protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            EnsureChildControls();

            this.PipelineEnabled = !FetchFromParent || (FetchFromParent && TransferStyle == TransferDataStyle.PersistanceService);
        }

		protected override void CreateChildControls()
        {
            ctrl_SerializedObject = new HiddenField();
            this.Controls.Add(ctrl_SerializedObject);
        }

		protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            if (this.TransferStyle == TransferDataStyle.PersistanceService && !IsViewModelFetched)
            {
                IPersistanceService service = FormsContext.Current.PersistanceService;
                this.ForceSetViewModel(GetDeserializedObject(service.LoadBinaryData(PersistanceIdentifier, PersistanceIdentifierKey)));

                PipelineEnabled = true;
                IsViewModelFetched = true;
            }
            else
            {
                if (this.IsPostBack && !IsViewModelFetched)
                {
                    this.ViewModel = GetDeserializedObject(Control_SerializedObject.Value);
                    ConnectViewModel(ViewModel);

                    Control_SerializedObject.Value = string.Empty;

                    PipelineEnabled = true;
                    IsViewModelFetched = true;
                }
            }
        }

		protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            if (!IsViewModelFetched)
            {
                FormsUtility.ExecuteStartupScript(this, (string.Format("Carbon.Popup.RetrieveData('{0}', '{1}');", SerializedObjectFieldId, Control_SerializedObject.ClientID)));
            }

            if (IsClosing)
            {
                if (this.TransferStyle == TransferDataStyle.Client)
                {
                    this.Control_SerializedObject.Value = GetSerializedObject(ViewModel);
                }
                else if (this.TransferStyle == TransferDataStyle.PersistanceService)
                {
                    IPersistanceService service = FormsContext.Current.PersistanceService;
                    Guid key = Guid.NewGuid();
                    Object identifier = service.StoreBinaryData(key, GetSerializedObjectToBytes(ViewModel));
                    String serializedIdentifier = InternalUtility.CompactSerialize(identifier);
                    this.Control_SerializedObject.Value = serializedIdentifier + "," + key;
                }
                else
                {
                    throw new ArgumentOutOfRangeException("this.TransferStyle"); 
                }
            }
        }

		public override void RenderControl(HtmlTextWriter writer)
        {
            if (!IsViewModelFetched || IsClosing)
                Control_SerializedObject.RenderControl(writer);

            if (IsClosing)
            {
                writer.AddStyleAttribute(HtmlTextWriterStyle.Display, "none");
                writer.RenderBeginTag(HtmlTextWriterTag.Div);
            }

            if (IsViewModelFetched)
                base.RenderControl(writer);

            if (IsClosing)
            {
                writer.RenderEndTag();
            }
        }

		#endregion

		#region Methods (11)

		protected override void AddParsedSubObject(object obj)
        {
            m_ChildControls.Add((Control)obj);
        }

		public void ClosePopup(bool returnViewModel)
        {
            OnClosePopup(this, new ClosePopupEventArgs(returnViewModel));
        }

		protected override TViewModel CreateViewModel()
        {
            throw new InvalidOperationException();
        }

		public TViewModel GetDeserializedObject(byte[] value)
        {
            return InternalUtility.CompactDeserializeFromByteArray<TViewModel>(value);
        }

		public TViewModel GetDeserializedObject(String value)
        {
            return InternalUtility.CompactDeserialize<TViewModel>(value);
        }

		public String GetSerializedObject(TViewModel value)
        {
            return InternalUtility.CompactSerialize<TViewModel>(value);
        }

		public byte[] GetSerializedObjectToBytes(TViewModel value)
        {
            return InternalUtility.CompactSerializeToByteArray<TViewModel>(value);
        }

		protected override bool OnBubbleEvent(object source, EventArgs args)
        {
            if (args is ClosePopupEventArgs)
            {
                OnClosePopup(source, (ClosePopupEventArgs)args);
                return true;
            }

            return base.OnBubbleEvent(source, args);
        }

		public virtual void OnClosePopup(object sender, ClosePopupEventArgs args)
        {
            if (((ClosePopupEventArgs)args).ReturnModel)
            {
                FormsUtility.ExecuteStartupScript(this, (string.Format("Carbon.Popup.ReturnData('{0}', '{1}');", SerializedObjectFieldId, Control_SerializedObject.ClientID)));
            }
            else
            {
                FormsUtility.ExecuteStartupScript(this, "Carbon.Popup.ClosePopup();");
            }

            IsClosing = true;
        }

		public override void OnUpdateControl(object sender, UpdateControlEventArgs args)
        {
            base.OnUpdateControl(sender, args);

            if (m_ControlsAdded)
                return;

            foreach (Control c in m_ChildControls)
            {
                this.Controls.Add(c);
            }

            m_ControlsAdded = true;
        }

		private void RaiseEventButFirstTryToHandle(object sender, EventArgs args)
        {
            if (!OnBubbleEvent(sender, args))
                RaiseBubbleEvent(sender, args);
        }

		#endregion
    }
}
